package com.fxnotes.book.presenter;

import com.fxnotes.Resources;
import com.fxnotes.command.OpenBook;
import com.fxnotes.event.RemoveBookEvent;
import com.fxnotes.event.UpdateBookEvent;
import com.fxnotes.book.node.BooksNode;
import com.fxnotes.common.thread.Executable;
import com.fxnotes.common.thread.TaskExecutor;
import com.fxnotes.common.ui.NodePresenter;
import com.google.common.base.Strings;
import com.google.common.eventbus.EventBus;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import com.fxnotes.database.inmemory.repository.EntityDuplicateException;
import com.fxnotes.domain.model.Book;
import com.fxnotes.domain.repository.IBookRepository;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.scene.Parent;

@Singleton
public class BooksPresenter extends NodePresenter<Parent> {

    private final EventBus eventBus;
    private final IBookRepository bookRepository;
    private final Provider<TaskExecutor> tasksProvider;
    private BooksNode booksNode;
    private static final Logger logger = Logger.getLogger(BooksPresenter.class.getName());

    @Inject
    public BooksPresenter(IBookRepository bookDepository, EventBus eventBus, Provider<TaskExecutor> tasksProvider) {
        super(Resources.PRESENTER_BOOKS);
        this.eventBus = eventBus;
        this.bookRepository = bookDepository;
        this.tasksProvider = tasksProvider;
    }

    // API
    @Inject
    public void setNode(BooksNode node) {
        this.booksNode = node;
    }

    @Override
    public Parent getNode() {
        return booksNode.getNode();
    }

    public void loadBooks() {
        reloadBooks();
    }

    public void addBook(final String name) {
        if (Strings.isNullOrEmpty(name)) {
            reportError("Empty book name!");
            return;
        }

        executeTask(new Executable() {

            private Book book;

            @Override
            public void execute() throws EntityDuplicateException {
                logger.log(Level.INFO, "Saving book {0}...", name);
                book = new Book(name);
                bookRepository.add(book);
            }

            @Override
            public void onSuccess() {
                reloadBooks();
            }

            @Override
            public void onFail(String message) {
                reportError(message);
                logger.severe(message);
            }
        });
    }

    public void updateBook(final Book book, final String name) {
        if (Strings.isNullOrEmpty(name)) {
            reportError("Empty book name!");
            return;
        }

        if (book.getName().equals(name)) {
            reportError("Book is unchanged!");
            return;
        }

        executeTask(new Executable() {

            @Override
            public void execute() throws EntityDuplicateException {
                logger.log(Level.INFO, "Updating book {0} with name {1}...", new Object[]{book, name});
                bookRepository.updateName(book, name);
            }

            @Override
            public void onSuccess() {
                eventBus.post(new UpdateBookEvent(book));
            }

            @Override
            public void onFail(String message) {
                reloadBooks();
                reportError(message);
            }
        });
    }

    public void removeBook(final Book book) {
        executeTask(new Executable() {

            @Override
            public void execute() throws Exception {
                logger.log(Level.INFO, "Removing book {0}...", book);
                bookRepository.remove(book);
            }

            @Override
            public void onSuccess() {
                eventBus.post(new RemoveBookEvent(book));
                reloadBooks();
            }

            @Override
            public void onFail(String message) {
                reloadBooks();
                reportError(message);
            }
        });
    }

    public void openBook(Book book) {
        if (book != null) {
            eventBus.post(new OpenBook(book));
        }
    }

    // helper methods
    private void executeTask(Executable executable) {
        tasksProvider.get().execute(executable);
    }

    private void reloadBooks() {
        executeTask(new Executable() {

            List<Book> books = new ArrayList<>();

            @Override
            public void execute() throws Exception {
                logger.log(Level.INFO, "Reloading books...");
                books.addAll(bookRepository.findAll());
            }

            @Override
            public void onSuccess() {
                logger.log(Level.INFO, "Books were reloaded.");
                booksNode.reloadBooks(books);
            }

            @Override
            public void onFail(String message) {
                logger.severe(message);
                booksNode.reloadBooks(books);
            }
        });
    }

    private void reportError(String message) {
        booksNode.reportError(message);
        logger.severe(message);
    }
}